<?php


namespace App\Service;

use App\Model\Dao\ChannelCollectionMchConnDao;
use App\Model\Dao\MchAccountDao;
use App\Model\Dao\MchConfigDao;
use App\Model\Dao\MchInfoDao;
use App\Model\Dao\TradeOrderDetailDao;
use App\Model\Enum\PayTypeEnum;
use App\Model\Enum\StatusEnum;
use App\Service\PayApi\PayService;
use App\Utils\CreditCardUtils;
use App\Utils\CustomUtils;
use App\Utils\SignUtils;
use Hyperf\Contract\ContainerInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Utils\ApplicationContext;
use Hyperf\Utils\Arr;

/**
 * Class MchPayService
 * 商户支付API
 *
 * @package App\Service
 */
class MchPayService {

    /**
     * @var MchInfoStatusService
     * @Inject()
     */
    protected $MchInfoStatusService;

    /**
     * @var MchConfigStatusService
     * @Inject()
     */
    protected $MchConfigStatusService;

    /**
     * @var MchAccountStatusService
     * @Inject()
     */
    protected $MchAccountStatusService;

    /**
     * @var MchInfoDao
     * @Inject()
     */
    protected $MchInfoDao;

    /**
     * @var MchConfigDao
     * @Inject()
     */
    protected $MchConfigDao;

    /**
     * @var MchAccountDao
     * @Inject()
     */
    protected $MchAccountDao;

    /**
     * @var SysCardTypeInfoService
     * @Inject()
     */
    protected $SysCardTypeInfoService;

    /**
     * @var ChannelCollectionMchConnDao
     * @Inject()
     */
    protected $ChannelCollectionMchConnDao;


    /**
     * @var PayService
     * @Inject()
     */
    private $PayService;

    public function getMchPayInfo ($param) {

        $mchInfo   = $param['mchInfo'];
        $mchConfig = $param['mchConfig'];

        $credential      = $this->_createCredential($mchInfo);
        $currencyOptions = $this->currencyOptions($param)['data'];
        $payTypeOptions  = $this->payTypeOptions(['mchNo' => $mchInfo['mchNo']])['data'];

        $data = [
            'mchInfo'         => [
                'mchName'        => $mchInfo['mchName'],
                'settleCurrency' => $mchConfig['settleCurrency'],
            ],
            'credential'      => $credential,
            'currencyOptions' => $currencyOptions,
            'payTypeOptions'  => $payTypeOptions,
            'cardTypeOptions' => $this->SysCardTypeInfoService->getOption(['status' => StatusEnum::ON])['data'],
        ];

        return CustomUtils::buildSuccess($data);
    }

    public function userPay ($params) {
        // 逻辑跟直连支付相同
        return $this->doPay($params);
    }

    private function _createCredential ($mchInfo) {

        $appId = $mchInfo['appId'];
        $ttl   = (int) env('PAY_TOKEN_TTL', 900);
        $token = $this->MchInfoDao->createUserPayToken($appId, $ttl);

        $credential = [
            'appId' => $appId,
            'token' => $token,
            'ttl'   => $ttl,
        ];

        return $credential;
    }

    public function verifyCreditCard ($param) {

        $cardNo      = $param['cardNo'];
        $cardType    = $param['cardType'];
        $cardCvv     = $param['cardCvv'];
        $cardExpireY = $param['cardExpireY'];
        $cardExpireM = $param['cardExpireM'];

        if (!$cardType) {
            // 未传卡种,猜测模式
            $card = CreditCardUtils::validCreditCard($cardNo);

            if (!$card['valid']) {
                throw new \Exception(trans('pay.account.credit.unknown_type'));
            }
        }
        else {

            // 校验模式
            $card = CreditCardUtils::validCreditCard($cardNo, $cardType);

            if (!$card['valid']) {
                throw new \Exception(trans('pay.account.credit.mismatched'));
            }

            if (!!$cardCvv && !CreditCardUtils::validCvc($cardCvv, $cardType)) {
                throw new \Exception(trans('pay.account.credit.cvv_fail'));
            }

            if (!!$cardExpireY && $cardExpireM && !CreditCardUtils::validDate($cardExpireY, $cardExpireM)) {
                throw new \Exception(trans('pay.account.credit.date_fail'));
            }
        }

        // 参数归一化
        $data = [
            'cardNo'   => $card['number'],
            'cardType' => $card['type'],
        ];

        return CustomUtils::buildSuccess($data);

    }

    /**
     * 平台支持币种列表
     *
     * @param $params
     *
     * @return array
     */
    public function currencyOptions ($params) {
        $mchConfig = $params['mchConfig'];

        $currencyOptions = explode(',', $mchConfig['transCurrency']);
        $currencyOptions = array_map(function ($currency) {
            return [
                'value' => $currency,
                'label' => $currency,
            ];
        }, $currencyOptions);

        $data = $currencyOptions;
        return CustomUtils::buildSuccess($data);
    }

    public function payTypeOptions ($params) {
        $mchNo = Arr::get($params, 'mchInfo.mchNo');

        $options = $this->ChannelCollectionMchConnDao->getMchPayTypeOptions($mchNo);

        $data = $options;

        return CustomUtils::buildSuccess($data);
    }

    /**
     * 支付下单
     *
     * @param $params
     *
     * @return array
     * @throws \Throwable
     */
    public function doPay ($params) {

        // 创建预支付订单
        $order = $this->PayService->createPreOrder($params);

        return $this->PayService->payPreOrder($params['mch'], $order, $params['payType'], $params['payAccount']);
    }

    /**
     * 跳转支付, 创建预支付订单
     *
     * @param $params
     *
     * @throws \Exception
     * @throws \Throwable
     */
    public function doPayJump ($params) {

        // 创建预支付订单
        $re = $this->PayService->createPreOrder($params);

        $orderDetail = $re['orderDetail'];
        $orderBase   = $re['orderBase'];

        $query = [
            'tradeNo'          => $orderDetail['tradeNo'],
            'mchTransCurrency' => $orderDetail['mchTransCurrency'],
            'mchTransAmount'   => $orderDetail['mchTransAmount'],
            'returnUrl'        => $orderBase['returnUrl'],
            'grEmail'          => Arr::get($params, 'order.grEmail'),
        ];

        $query = $this->_parseSignQuery($params['mch'], $query);

        $payJumpUrl = sprintf('%s?%s', $this->_getPayJumpUrl(), http_build_query($query));

        $data = [
            'payJumpUrl' => $payJumpUrl,
            'query'      => $query,
        ];

        return CustomUtils::buildSuccess($data);
    }

    protected function _parseSignQuery ($mch, $query) {
        $query = array_merge($query, [
            'appId'     => Arr::get($mch, 'mchInfo.appId'),
            'mchUrl'    => Arr::get($mch, 'mchUrl'),
            'timestamp' => time(),
        ]);

        $query['sign'] = SignUtils::makeSign($query, Arr::get($mch, 'mchInfo.appSecret'));

        return $query;
    }

    protected function _getPayJumpUrl () {
        return env('PAY_JUMP_URL');
    }

    /**
     * 支付
     *
     * @param $params
     *
     * @return array
     * @throws \Throwable
     */
    public function doPayJumpConfirm ($params) {

        // 读取预支付订单
        $order = $this->PayService->getPreOrder($params);

        // 支付
        return $this->PayService->payPreOrder($params['mch'], $order, $params['payType'], $params['payAccount']);
    }


}
